package com.netx.generics.translation;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;

import com.netx.generics.basic.Checker;
import com.netx.generics.basic.ErrorList;
import com.netx.generics.util.Strings;
import com.netx.generics.collections.ImmutableList;


public class Results {

	private final Translator _translator;
	private Object _contents;
	private boolean _blocked;
	private final List<Message> _errors;
	private final List<Message> _warnings;
	
	// for Translator:
	Results(Translator t, Object initialObject) {
		_translator = t;
		_contents = initialObject;
		_blocked = false;
		_errors = new ArrayList<Message>();
		_warnings = new ArrayList<Message>();
	}

	public Translator getTranslator() {
		return _translator;
	}

	public Object getContents() {
		return _contents;
	}
	
	public boolean hasErrors() {
		return !_errors.isEmpty();
	}

	public boolean hasWarnings() {
		return !_warnings.isEmpty();
	}
	
	public ErrorList getMessages(String eHeader, String wHeader, MessageFormatter mf) {
		Checker.checkEmpty(eHeader, "eHeader");
		Checker.checkNull(mf, "mf");
		List<String> el = _format(_errors, mf);
		if(Strings.isEmpty(wHeader)) {
			return new ErrorList(eHeader, el);
		}
		else {
			List<String> wl = _format(_warnings, mf);
			return new ErrorList(eHeader, el, wHeader, wl);
		}
	}

	public ErrorList getMessages(String eHeader, MessageFormatter mf) {
		return getMessages(eHeader, null, mf);
	}

	public ErrorList[] getMessagesByStep(String[] eHeaders, String[] wHeaders, MessageFormatter mf) {
		Checker.checkNull(mf, "mf");
		Checker.checkEmpty(eHeaders, "eHeaders");
		Checker.checkEmptyElements(eHeaders, "eHeaders");
		final int stepCount = getTranslator().getTotalStepCount();
		Checker.checkLength(eHeaders, stepCount, "eHeaders");
		if(wHeaders != null) {
			Checker.checkEmpty(wHeaders, "wHeaders");
			Checker.checkEmptyElements(wHeaders, "wHeaders");
			Checker.checkLength(wHeaders, stepCount, "wHeaders");
		}
		// Create lists for separated messages:
		@SuppressWarnings("unchecked")
		List<String>[] eArray = new List[stepCount];
		@SuppressWarnings("unchecked")
		List<String>[] wArray = new List[stepCount];
		for(int i=0; i<stepCount; i++) {
			eArray[i] = new ArrayList<String>();
			if(wHeaders != null) {
				wArray[i] = new ArrayList<String>();
			}
		}
		// Separate messages by step:
		Iterator<Message> it = _errors.iterator();
		while(it.hasNext()) {
			Message m = it.next();
			eArray[m.getStepNumber()].add(mf.format(m));
		}
		if(wHeaders != null) {
			it = _warnings.iterator();
			while(it.hasNext()) {
				Message m = it.next();
				wArray[m.getStepNumber()].add(mf.format(m));
			}
		}
		// Create ErrorList objects:
		ErrorList[] results = new ErrorList[stepCount];
		for(int i=0; i<stepCount; i++) {
			List<String> errors = eArray[i];
			List<String> warnings = wArray[i];
			if(wHeaders != null) {
				if(errors.isEmpty() && warnings.isEmpty()) {
					results[i] = null;
				}
				else {
					results[i] = new ErrorList(eHeaders[i], errors, wHeaders[i], warnings);
				}
			}
			else {
				if(errors.isEmpty()) {
					results[i] = null;
				}
				else {
					results[i] = new ErrorList(eHeaders[i], errors);
				}
			}
		}
		return results;
	}

	public ErrorList[] getMessagesByStep(String[] eHeaders, MessageFormatter mf) {
		return getMessagesByStep(eHeaders, null, mf);
	}

	public void addError(String message, Position p) {
		_checkBlocked();
		Checker.checkNull(message, "message");
		_errors.add(new Message(message.trim(), p, _translator.getPerformedStepCount()));
	}

	public void addError(String message) {
		addError(message, null);
	}

	public void addWarning(String message, Position p) {
		_checkBlocked();
		Checker.checkNull(message, "message");
		_warnings.add(new Message(message.trim(), p, _translator.getPerformedStepCount()));
	}

	public void addWarning(String message) {
		addWarning(message, null);
	}
	
	public void addMessages(String errorPrefix, String warningPrefix, ErrorList el) {
		Checker.checkNull(el, "el");
		if(el.getErrors() != null) {
			Iterator<String> it = el.getErrors().iterator();
			while(it.hasNext()) {
				addError((Strings.isEmpty(errorPrefix) ? "" : errorPrefix) + it.next().trim());
			}
		}
		if(el.getWarnings() != null) {
			Iterator<String> it = el.getWarnings().iterator();
			while(it.hasNext()) {
				addWarning((Strings.isEmpty(warningPrefix) ? "" : warningPrefix) + it.next().trim());
			}
		}
	}

	public void addMessages(ErrorList el, String errorPrefix) {
		addMessages(errorPrefix, null, el);
	}

	public void addMessages(ErrorList el) {
		addMessages(null, null, el);
	}

	// for Translator:
	void setContents(Object o) {
		_contents = o;
	}

	// for Translator:
	Results block() {
		_blocked = true;
		return this;
	}

	private ImmutableList<String> _format(List<Message> l, MessageFormatter mf) {
		Iterator<Message> it = l.iterator();
		List<String> formatted = new ArrayList<String>(l.size());
		while(it.hasNext()) {
			formatted.add(mf.format(it.next()));
		}
		return new ImmutableList<String>(formatted);
	}
	
	private void _checkBlocked() {
		if(_blocked) {
			throw new IllegalStateException("object has been blocked and cannot be modified");
		}
	}

}
